﻿//Copyright (c) 2009, Offbeat Guides
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification, are 
//permitted provided that the following conditions are met:
//
//* Redistributions of source code must retain the above copyright notice, this list of 
//conditions and the following disclaimer.
//
//* Redistributions in binary form must reproduce the above copyright notice, this list 
//of conditions and the following disclaimer in the documentation and/or other materials 
//provided with the distribution.
//
//* Neither the name of Offbeat Guides nor the names of its contributors may be used to 
//endorse or promote products derived from this software without specific prior written 
//permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
//EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
//SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
//TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Schema;
using System.Xml;
using System.Xml.Serialization;
using System.Text.RegularExpressions;
using System.IO;

namespace Amazon.Services.S3
{
    [Serializable]
    public abstract class FileSystemInfo : System.Xml.Serialization.IXmlSerializable
    {
        #region Static Variables
        internal static readonly System.Int32 MinPathLength = 3;
        internal static readonly System.Int32 MaxPathLength = 1024;
        #endregion

        #region Variables
        [NonSerialized]
        private Amazon.Services.ServiceClient _client;
        private System.String _bucket;
        private System.String _path;
        private System.String _name;
        #endregion

        #region Properties
        internal ServiceClient Client
        {
            get { return this._client; }
        }

        protected String Bucket
        {
            get { return this._bucket; }
        }

        protected virtual String Path
        {
            get { return this._path; }
        }

        public virtual String Name
        {
            get { return this._name; }
        }

        public String FullName
        {
            get { return String.Format("{0}:\\{1}", this.Bucket, this.Path.Replace('/', '\\')); }
        }

        public Boolean Exists { get; protected set; }

        public DateTime CreationTime { get; protected set; }

        public DateTime LastAccessTime { get; protected set; }

        public DateTime LastWriteTime { get; protected set; }
        #endregion

        #region Constructors and Finalizers
        internal FileSystemInfo(ServiceClient client, String fullpath)
        {
            if (client == null)
                throw new ArgumentNullException("client");
            if (String.IsNullOrEmpty(fullpath))
                throw new ArgumentNullException("fullpath");

            Match match = FileSystemHelper.PathRegex.Match(fullpath);
            if (!match.Success)
                throw new ArgumentException(String.Format("Unable to locate the directory specified. directory = \"{0}\".", fullpath));

            this._client = client;

            this._bucket = match.Groups["bucket"].Value;
            if (this._bucket.Length > DriveInfo.MaxNameLength || this._bucket.Length < DriveInfo.MinNameLength)
                throw new ArgumentException("Drive length is beyond the accepted length.");

            this._path = FileSystemHelper.NormalizePath(match.Groups["path"].Value);
            if (this._path.Length > DirectoryInfo.MaxPathLength || this._path.Length < DirectoryInfo.MinPathLength)
                throw new ArgumentException("Path length is beyond the accepted length.");

            this._name = match.Groups["name"].Value;

            this.Refresh();
        }

        internal FileSystemInfo(ServiceClient client, String bucket, String path)
        {
            if (client == null)
                throw new ArgumentNullException("client");
            if (String.IsNullOrEmpty(bucket))
                throw new ArgumentNullException("bucket");
            if (String.IsNullOrEmpty(path))
                throw new ArgumentNullException("path");

            this._client = client;

            this._bucket = bucket;
            if (this._bucket.Length > DriveInfo.MaxNameLength || this._bucket.Length < DriveInfo.MinNameLength)
                throw new ArgumentException("Drive length is beyond the accepted length.");

            this._path = FileSystemHelper.NormalizePath(path);
            if (this._path.Length > DirectoryInfo.MaxPathLength || this._path.Length < DirectoryInfo.MinPathLength)
                throw new ArgumentException("Path length is beyond the accepted length.");

            Int32 index = this._path.LastIndexOf('/');
            this._name = index == -1 ? this._path : this._path.Substring(index).Trim('/');

            this.Refresh();
        }
        #endregion

        #region Methods
        public virtual void Refresh()
        {
            if (this._client == null)
                throw new IOException();

            try
            {
                Dictionary<String, String> parameters = new Dictionary<String, String>();
                parameters.Add("bucket", this._bucket);
                parameters.Add("path", this._path);
                parameters.Add("method", "HEAD");

                Response response = this._client.Invoke(parameters, null);
                if (response.Status != System.Net.HttpStatusCode.OK)
                    throw new IOException("Unable to find file");

                this.Exists = true;
                this.LastWriteTime = DateTime.Now;
                if (response.Headers.ContainsKey("last-modified"))
                    this.LastWriteTime = DateTime.Parse(response.Headers["last-modified"]);
            }
            catch (AmazonException ex)
            {
                if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
                    this.Exists = false;
                else
                    throw;
            }
        }

        public abstract void Delete();
        #endregion

        #region Overriden Methods
        public override string ToString()
        {
            return this.FullName;
        }
        public override bool Equals(object obj)
        {
            FileSystemInfo info = obj as FileSystemInfo;
            return (info != null && this.FullName.Equals(info.FullName, StringComparison.OrdinalIgnoreCase));
        }
        #endregion

        #region IXmlSerializable Methods
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            throw new NotImplementedException();
        }

        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}
